#include "DHT.h"
#include "Supervisor.h"
#include "hw/board.hpp"
#include "hw/timer9.h"
#include <cassert>

using namespace actor;

auto constexpr get_self = []() { return sup.get_child<1>(); };
auto constexpr get_uart_id = []() { return sup.get_child<0>()->get_id(); };

template<int SZ> inline void format(char* ptr, uint8_t v) {
    for (int i = 0; i < SZ; ++i) {
      auto r = v % 10;
      v /= 10;
      *ptr-- = r + '0';
    }
}

void DHT::initialize() {
  hw::Board::sensor_pin_output(1);
  send<rl::ctx::thread, message::LogStatic>(0, get_uart_id(),
                                            "[dht] init, sending high\r\n");

  subscribe(&DHT::on_request);
  Parent::initialize();
}

void DHT::advance_init() {
  /*
  When power is supplied to the sensor, do not send any
  instruction to the sensor in within one second in order to pass the unstable
  status.
  */
  add_event<rl::ctx::thread>(
      1500000,
      [](void *) {
        get_self()->substate = Substate::none;
        get_self()->Parent::advance_init();
      },
      this);
}

void DHT::on_timer() {
  if (substate == Substate::none) {
    handshake_continue();
  } else if (substate == Substate::hs_keep_high) {
    handshake_wait_low();
  }
}

void DHT::on_request(message::RequestHT &) {
  send<rl::ctx::thread, message::LogStatic>(0, get_uart_id(),
                                            "[dht] init, sending low\r\n");
  hw::Board::sensor_pin_out<0>();
  // Data Single-bus voltage level from high to low and this process must take
  // at least 18ms to ensure DHT’s detection of MCU's signal 168M * 0.020 =
  // 380*8000
  hw::Timer9::await(379, 7999);
}

void DHT::handshake_continue() {
  send<rl::ctx::interrupt, message::LogStatic>(0, get_uart_id(),
                                               "[dht] hs, sending high\r\n");
  hw::Board::sensor_pin_out<1>();
  substate = Substate::hs_keep_high;
  // 168M*20u = 3360 = 56 * 60
  hw::Timer9::await(55, 59);
}

void DHT::handshake_wait_low() {
  send<rl::ctx::interrupt, message::LogStatic>(0, get_uart_id(),
                                               "[dht] hs wait low\r\n");
  substate = Substate::hs_wait_low;
  hw::Board::sensor_pin_out<1>();
  hw::Board::sensor_pin_input();
  hw::Timer9::cancel();
  hw::Timer9::reset(167);
}

void DHT::on_edge() {
  static constexpr uint_fast8_t SZ = 7;
  auto prev = model.prev;
  auto passed = hw::Timer9::read_reset();

  auto value = hw::Board::sensor_value();
  char msg[SZ] = {' ', ' ', '0', '0', '+', '\r', '\n'};
  if (value) {
    msg[4] = '-';
  }
  format<2>(msg + 3, static_cast<uint8_t>(passed));
  if (value) {
    msg[0] = ' ';
  } else {
    msg[0] = (passed > prev) ? '1' : '0';
  }
  send<rl::ctx::interrupt, message::LogDynamic>(0, get_uart_id(), msg, SZ);
  auto done = model.toggle(passed);
  if (done) {
    hw::Board::sensor_pin_output(1);
    send<rl::ctx::interrupt, message::LogStatic>(0, get_uart_id(),
                                                 "[dht] done\r\n");
    auto opt = model::DHT11::construct(model);
    if (!opt) {
        send<rl::ctx::interrupt, message::LogStatic>(0, get_uart_id(),
                                                     "[dht] checksum error\r\n");
    } else {
        static constexpr uint_fast8_t BUFF_SZ = 11;
        auto& val = opt.value();
        char buff[BUFF_SZ] = {'t', ' ', '=', ' ', '0', '0', '.', '0', '0', '\r', '\n'};
        auto& t = val.temperature();
        format<2>(buff + 5, t.integral);
        format<2>(buff + 8, t.decimal);
        send<rl::ctx::interrupt, message::LogDynamic>(0, get_uart_id(), buff, BUFF_SZ);

        buff[0] = 'h';
        auto& h = val.humidity();
        format<2>(buff + 5, h.integral);
        format<2>(buff + 8, h.decimal);
        send<rl::ctx::interrupt, message::LogDynamic>(0, get_uart_id(), buff, BUFF_SZ);
    }
  }
}

extern "C" {

void exti1_irq_handler_backend() { get_self()->on_edge(); }

void tim1_brk_tim9_irq_handler_backend() { get_self()->on_timer(); }
}
